package com.livepan.server.utils;

import com.livepan.server.error.BusinessException;
import com.livepan.server.service.RecordService;

import java.util.*;

public class RecommendUtil {
    /**
     * 协同过滤计算,返回用户最接近的questionId
     * 如果没有可推荐的题目，那么返回-1
     */
    public static Integer recommend(Map<Integer, List<Integer>> user_question, String userId, RecordService recordService) throws BusinessException {

        int N = user_question.size();
        //建立用户稀疏矩阵，用于用户相似度计算【相似度矩阵】
        int[][] sparseMatrix = new int[N][N];

        //存储每一个用户对应的做错的题目的总数 eg: A 3
        Map<Integer, Integer> userItemLength = new HashMap<>();

        //建立错题到用户的倒排表 eg: a A B
        Map<Integer, Set<Integer>> itemUserCollection = new HashMap<>();

        //辅助存储物品集合
        Set<Integer> items = new HashSet<>();

        ////辅助存储每一个用户的用户ID映射
        Map<Integer, Integer> userID = new HashMap<>();

        ////辅助存储每一个ID对应的用户映射
        Map<Integer, Integer> idUser = new HashMap<>();

//        for (int i = 0; i < N; i++) {
//            //依次处理N个用户 输入数据 以空格间隔
//            int user = allUser[i];
//            int length = questions[i].length;
//            //eg: A 3
//            userItemLength.put(user, length);
//
//            //用户ID与稀疏矩阵建立对应关系
//            userID.put(user, i);
//            idUser.put(i, user);
//
//            //建立物品--用户倒排表
//            for (int j = 0; j < length; j++) {
//                if (items.contains(questions[i][j])) {
//                    //如果已经包含对应的物品--用户映射，直接添加对应的用户
//                    itemUserCollection.get(questions[i][j]).add(user);
//                } else {
//                    //否则创建对应物品--用户集合映射
//                    items.add(questions[i][j]);
//                    itemUserCollection.put(questions[i][j], new HashSet<>());
//                    //创建物品--用户倒排关系
//                    itemUserCollection.get(questions[i][j]).add(user);
//                }
//            }
//        }

        Set<Map.Entry<Integer, List<Integer>>> userQuestionSet = user_question.entrySet();
        Iterator<Map.Entry<Integer, List<Integer>>> userQuestionIterator = userQuestionSet.iterator();
        int label = 0;
        while (userQuestionIterator.hasNext()) {
            Map.Entry<Integer, List<Integer>> next = userQuestionIterator.next();
            int user = next.getKey();
            int length = next.getValue().size();
            //eg: A 3
            userItemLength.put(user, length);

            //用户ID与稀疏矩阵建立对应关系
            userID.put(user, label);
            idUser.put(label, user);

            //建立物品--用户倒排表
            for (int j = 0; j < length; j++) {
                if (items.contains(next.getValue().get(j))) {
                    //如果已经包含对应的物品--用户映射，直接添加对应的用户
                    itemUserCollection.get(next.getValue().get(j)).add(user);
                } else {
                    //否则创建对应物品--用户集合映射
                    items.add(next.getValue().get(j));
                    itemUserCollection.put(next.getValue().get(j), new HashSet<>());
                    //创建物品--用户倒排关系
                    itemUserCollection.get(next.getValue().get(j)).add(user);
                }
            }
            label++;
        }


        System.out.println(itemUserCollection.toString());
        //计算相似度矩阵【稀疏】
        Set<Map.Entry<Integer, Set<Integer>>> entrySet = itemUserCollection.entrySet();
        Iterator<Map.Entry<Integer, Set<Integer>>> iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Set<Integer> commonUsers = iterator.next().getValue();
            for (Integer user_u : commonUsers) {
                for (Integer user_v : commonUsers) {
                    if (user_u.equals(user_v)) {
                        continue;
                    }
                    sparseMatrix[userID.get(user_u)][userID.get(user_v)] += 1;
                    //计算用户u与用户v都有正反馈的物品总数
                }
            }
        }
        System.out.println(userItemLength.toString());
        System.out.println("Input the user for recommendation:<eg:A>");
        System.out.println(userID.get(userId));
        //计算用户之间的相似度【余弦相似性】
        int recommendUserId = userID.get(userId);
        for (int j = 0; j < sparseMatrix.length; j++) {
            if (j != recommendUserId) {
                System.out.println("User:" + idUser.get(recommendUserId) + " -- User:" + idUser.get(j) + "相似度:" +
                        sparseMatrix[recommendUserId][j] / Math.sqrt(userItemLength.get(idUser.get(recommendUserId)) * userItemLength.get(idUser.get(j))));
            }
        }

        double similarity = 0;
        int similarityQuestionId = -1;
        //计算指定用户recommendUser的物品推荐度
        for (Integer item : items) {
            //遍历每一件物品
            Set<Integer> users = itemUserCollection.get(item);
            if (recordService.is_down(userId, item)) {
                continue;
            }
            //得到购买当前物品的所有用户集合
            if (!users.contains(userId)) {
                //如果被推荐用户没有购买当前物品，则进行推荐度计算
                double itemRecommendDegree = 0.0;
                for (Integer user : users) {
                    itemRecommendDegree += sparseMatrix[userID.get(userId)][userID.get(user)] / Math.sqrt(userItemLength.get(userId) * userItemLength.get(user));
                    //推荐度计算
                }
                System.out.println("The question " + item + " for " + userId + "'s recommended degree:" + itemRecommendDegree);
                if (similarity < itemRecommendDegree) {
                    similarity = itemRecommendDegree;
                    similarityQuestionId = item;
                }
            }
        }
        return similarityQuestionId;
    }

    public static void main(String args[]) {
//        Map<Integer, List<Integer>> wrongQuestion = new HashMap<>();
//        List<Integer> list1 = new ArrayList<>();
//        List<Integer> list2 = new ArrayList<>();
//        List<Integer> list3 = new ArrayList<>();
//        list1.add(1);
//        list2.add(1);
//        list2.add(2);
//        list2.add(3);
//        list3.add(1);
//        list3.add(2);
//        list3.add(3);
//        wrongQuestion.put(1, list1);
//        wrongQuestion.put(2, list2);
//        wrongQuestion.put(3, list3);
//        recommend(wrongQuestion,1);
    }
}
